#ifndef _HAVE_PACKET_H
#define _HAVE_PACKET_H

#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "field.h"
enum field_class {
    base,
    packet,
    array,
    bits,
    bit,
    N_FIELD_CLASS
};

struct packet_desc {
    char const *pkt_name;
    size_t pkt_size;
    size_t packed_size;
    size_t n_fields;

    size_t *field_offsets;
    size_t *field_sizes;
    size_t *field_class;
    char const **field_names;
    char const **field_types;
};

static struct field *field_init(struct packet_desc *desc);
/* inner-define
 * */
#define _DEFINE_base( type, name, size)  type name;
#define _DEFINE_ref(  type, name, size)  type name;
#define _DEFINE_array(type, name, size)  type name[size];
#define _DEFINE_bit(  type, name, size)  type name:size;
#define _DEFINE_bits( type, name, size)  \
        struct {\
            name(DEFINE) \
        }name;
#define _DEFINE_packet(type, name, size) \
    struct name { \
        name(DEFINE) \
    }name;

#define _SIZEOF_END(type, name, size) name),
#define SIZEOF_END(name) _SIZEOF_END
#define _SIZEOF_FIELD(name) sizeof(((struct name*)0)-> SIZEOF_END

#define _COUNT_FIELD( type, name, size) +1
#define _PACKED_END(type, name, size) name)
#define PACKED_END(name) _PACKED_END
#define _PACKED_FIELD(name) +sizeof(((struct name*)0)-> PACKED_END
#define _STRING_TYPE( type, name, size) #type,
#define _STRING_NAME( type, name, size) #name,
#define _NULL_FIELD(  type, name, size)
#define _OFFSET_END(  type, name, size) name),
#define OFFSET_END(name) _OFFSET_END
#define _OFFSET_FIELD(name) __builtin_offsetof(struct name, OFFSET_END

#define DEFINE(CLASS)       _DEFINE_##CLASS
#define COUNT_FIELD(CLASS)  _COUNT_FIELD
#define SIZEOF_FIELD(CLASS) _SIZEOF_FIELD(CLASS)
#define PACKED_FIELD(CLASS) _PACKED_FIELD(CLASS)
#define STRING_TYPE(CLASS)  _STRING_TYPE
#define STRING_NAME(CLASS)  _STRING_NAME
#define CLASS_FIELD(CLASS)  CLASS,_NULL_FIELD
#define OFFSET_FIELD(CLASS) _OFFSET_FIELD(CLASS)

#define TT(a, b, c) b),
#define _TEST(name) TT
#define TEST(name) func(struct name, _TEST

#define PACKET(name) \
    struct name{ \
        name(DEFINE) \
    }; \
struct packet_desc name##_desc = { \
    .pkt_name       = #name, \
    .pkt_size       = sizeof(struct name),\
    .n_fields       = name(COUNT_FIELD),\
    .packed_size    = name(PACKED_FIELD(name)),\
    .field_offsets  = (size_t[])      { name(OFFSET_FIELD(name))},\
    .field_sizes    = (size_t[])      { name(SIZEOF_FIELD(name)) },\
    .field_class    = (size_t[])      { name(CLASS_FIELD) },\
    .field_types    = (char const *[]){ name(STRING_TYPE) },\
    .field_names    = (char const *[]){ name(STRING_NAME) },\
};

static inline void define_printf(struct packet_desc *desc)
{
    size_t n_field = 0;
    printf("struct %s {\n", desc->pkt_name);
    for (n_field = 0; n_field < desc->n_fields; n_field++) {
        printf("\t%-16s%16s;\t/*%4d%4d */\n",
                desc->field_types[n_field],
                desc->field_names[n_field],
                desc->field_offsets[n_field],
                desc->field_sizes[n_field]
                );
    }
    printf("\n\t/* size: %d, members: %d */\n"
           "\t/* sum members: %d */\n",
           desc->pkt_size,
           desc->n_fields,
           desc->packed_size);
    printf("};\n");
}
static bool packet_field_get_by_name(struct packet_desc *desc,
        const char *name, struct field *field)
{
    size_t n_field = 0;
    for (n_field = 0; n_field < desc->n_fields; n_field++) {
        if (strcmp(desc->field_names[n_field], name) == 0) {
            field->name = (char *)name;
            field->type = (char *)desc->field_types[n_field];
            field->length = desc->field_sizes[n_field];
            return true;
        }
    }
    return false;
}

static inline void packet_set_by_name(struct packet_desc *desc, void *pkt, ...)
{
    va_list var_args;
    size_t argno = 0;
    char *name = NULL;
    void *value;
    size_t n_field = 0;
    va_start(var_args, pkt);
    while((name = va_arg(var_args, char *)) && argno < desc->n_fields) {
        printf("%s ", name);
        value = va_arg(var_args, void *);
        for (n_field = 0; n_field < desc->n_fields; n_field++) {
            if (strcmp(desc->field_names[n_field], name) == 0) {
                switch(desc->field_sizes[n_field]) {
                    default:
                    printf("%d\n", (int)value);
                    memcpy(pkt+desc->field_offsets[n_field], &value,
                            desc->field_sizes[n_field]);
                    break;
                }
            }

        }
        argno++;
   }
   va_end(var_args);
}

static inline void packet_set_by_offset(struct packet_desc*desc, void *pkt, ...)
{
}

#endif /* _HAVE_PACKET_H */
